﻿using System.Collections.Concurrent;

namespace Amber.Arch.Catty
{
    public class Cat : IServiceProvider, IDisposable
    {
        /// <summary>
        /// 根容器
        /// </summary>
        internal readonly Cat _root;

        /// <summary>
        /// 存储所有添加的服务注册，Key为服务类型，Value为ServiceRegistry链表对象
        /// </summary>
        internal readonly ConcurrentDictionary<Type, ServiceRegistry> _registries;

        /// <summary>
        /// ？存储当前Cat对象提供的非transient服务实例
        /// </summary>
        internal readonly ConcurrentDictionary<Key, object?> _services;

        /// <summary>
        /// 存储实现IDisposable的实例对象
        /// </summary>
        internal readonly ConcurrentBag<IDisposable> _disposables;

        private volatile bool _disposed;

        internal Cat()
        {
            _registries = new ConcurrentDictionary<Type, ServiceRegistry>();
            _root = this;
            _services = new ConcurrentDictionary<Key, object?>();
            _disposables = new ConcurrentBag<IDisposable>();
        }

        internal Cat(Cat parent)
        {
            _registries = parent._registries;
            _root = parent._root;
            _services = new ConcurrentDictionary<Key, object?>();
            _disposables = new ConcurrentBag<IDisposable>();
        }

        public object? GetService<TService>() => GetService(typeof(TService));

        public object? GetService(Type serviceType)
        {
            EnsureNotDisposed();
            if (serviceType == typeof(Cat) || serviceType == typeof(IServiceProvider))
            {
                return this;
            }

            ServiceRegistry? registry;
            // IEnumerable<T>类型
            if (serviceType.IsGenericType && serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                var elementType = serviceType.GetGenericArguments()[0];
                if (!_registries.TryGetValue(elementType, out registry))
                {
                    return Array.CreateInstance(elementType, 0);
                }

                var registries = registry.AsEnumerable();
                var services = registries.Select(x => GetServiceCore(x, Type.EmptyTypes)).Reverse().ToArray();
                var array = Array.CreateInstance(elementType, services.Length);
                services.CopyTo(array, 0);
                return array;
            }

            // Generic类型
            if (serviceType.IsGenericType && !_registries.ContainsKey(serviceType))
            {
                var definition = serviceType.GetGenericTypeDefinition();
                return _registries.TryGetValue(definition, out registry)
                    ? GetServiceCore(registry, serviceType.GetGenericArguments())
                    : null;
            }

            // 普通类型
            return _registries.TryGetValue(serviceType, out registry)
                ? GetServiceCore(registry, Array.Empty<Type>())
                : null;
        }

        public void Dispose()
        {
            _disposed = true;
            foreach (var disposable in _disposables)
            {
                disposable.Dispose();
            }
            _disposables.Clear();
            _services.Clear();
        }

        private void EnsureNotDisposed()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("Cat");
            }
        }

        public Cat Register(ServiceRegistry registry)
        {
            EnsureNotDisposed();
            if (_registries.TryGetValue(registry.ServiceType, out var existing))
            {
                _registries[registry.ServiceType] = registry;
                registry.Next = existing;
            }
            else
            {
                _registries[registry.ServiceType] = registry;
            }
            return this;
        }

        /// <summary>
        /// 提供服务实例的核心操作
        /// </summary>
        /// <param name="registry"></param>
        /// <param name="genericArguments"></param>
        /// <returns></returns>
        private object? GetServiceCore(ServiceRegistry registry, Type[] genericArguments)
        {
            Key key = new(registry, genericArguments);

            switch (registry.LifeTime)
            {
                // 如果生命周期为Root和Self
                case LifeTime.Root:
                    return GetOrCreate(_root._services, _root._disposables);
                case LifeTime.Self:
                    return GetOrCreate(_services, _disposables);
                default:
                    // 对于Transient模式，直接利用ServiceRegistry对象提供的工厂来创建服务实例
                    // 如果服务实例实现了IDisposable，则该服务实例会被添加到_disposables
                    var service = registry.Factory(this, genericArguments);
                    if (service is IDisposable disposable && disposable != this)
                    {
                        _disposables.Add(disposable);
                    }
                    return service;
            }

            object? GetOrCreate(ConcurrentDictionary<Key, object?> services, ConcurrentBag<IDisposable> disposables)
            {
                if (services.TryGetValue(key, out var service))
                {
                    return service;
                }
                service = registry.Factory(this, genericArguments);
                services[key] = service;
                if (service is IDisposable disposable)
                {
                    disposables.Add(disposable);
                }
                return service;
            }
        }
    }
}
